﻿using System;
using System.Net;
using System.Net.Sockets;
using Development.Tools;


namespace Development.Network.Tcp
{
    /// <summary>
    /// tcp 服务器
    /// </summary>
    public class TcpServer
    {
        /// <summary>
        /// 用于接收到消息的回调
        /// </summary>
        public event TcpMsgHandle EventRecvMsg;

        private const int SOKECT_TIMEOUT_MS = 30000;  //心跳超时检测时间
        private Socket _sokect;
        private System.Timers.Timer _heartTimer;   //心跳检测计时器
        private int _numConnections;
        private TcpTokenManager _tokenManager;   //连接管理

        public Socket SocketHandle
        {
            get { return _sokect; }
        }

        public TcpServer(int numConnections, int recvBufferSize)
        {
            _numConnections = numConnections;
            _tokenManager = new TcpTokenManager();
            for(int i = 0; i < numConnections;++i)
            {
                var token = new TcpToken(this,GUIDTool.LinkId, recvBufferSize);
                _tokenManager.Push(token);
            }
        }

        public TcpToken GetToken(int linkId)
        {
            return _tokenManager.GetUserToken(linkId);
        }

        /// <summary>
        /// 监听
        /// </summary>
        /// <param name="port"></param>
        /// <param name="ip"></param>
        /// <returns></returns>
        public bool Listen(int port, string ip = "")
        {
            IPEndPoint point = null;
            if (string.IsNullOrEmpty(ip))
            {
                point = new IPEndPoint(IPAddress.Any, port);
            }
            else
            {
                point = new IPEndPoint(IPAddress.Parse(ip), port);
            }
            try
            {
                _sokect = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _sokect.Bind(point);
                _sokect.Listen(_numConnections);
                Log.WriteLog(ELogLevel.LOG_INFO, string.Format("socket listen port: {0} success", port));
                for (int i = 0; i < 20; ++i)
                {
                    PostAccept(null);
                }
                StartHearJump();
            }
            catch (SocketException e)
            {
                Log.WriteLog(ELogLevel.LOG_ERROR,e);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 启动心跳检测
        /// </summary>
        private void StartHearJump()
        {
            _heartTimer = new System.Timers.Timer(SOKECT_TIMEOUT_MS);
            _heartTimer.Elapsed += _heartTimer_Elapsed;
            _heartTimer.Start();
            
        }
        /// <summary>
        /// 心跳检测
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _heartTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            var tokenList = _tokenManager.GetList();
            foreach (var token in tokenList)
            {
                if (token.IsSokectOutTime(SOKECT_TIMEOUT_MS))
                {
                    CloseToken(token);
                }
            }
        }

        /// <summary>
        /// 关闭用户
        /// </summary>
        /// <param name="token"></param>
        public void CloseToken(TcpToken token)
        {
             token.Close();
             int count = _tokenManager.Count() - 1;
             if (count < 0)
             {
                 count = 0;
             }
             Log.WriteLog(ELogLevel.LOG_INFO, string.Format("{0} out,num = {1}", token.RemoteEndPoint(), count));
            _tokenManager.Push(token);
        }
        /// <summary>
        /// 投递监听
        /// </summary>
        /// <param name="e"></param>
        private void PostAccept(SocketAsyncEventArgs args)
        {
            if (args == null)
            {
                args = new SocketAsyncEventArgs();
                args.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
            }
            else
            {
                args.AcceptSocket = null; //释放上次绑定的Socket，等待下一个Socket连接
            }
            try
            {
                bool willRaiseEvent = _sokect.AcceptAsync(args);
                if (!willRaiseEvent)
                {
                    OnAccept(args);
                }
            }
            catch (Exception) { }
        }

        private void OnAccept(SocketAsyncEventArgs e)
        {
            var userToken = _tokenManager.Pop();
            if (userToken == null)
            {
                //缓存已用尽，放掉连接
                Log.WriteLog(ELogLevel.LOG_INFO, "user token pool empty");
                e.AcceptSocket.Close();
                return;
            }
            userToken.InitToken(e.AcceptSocket);
            Log.WriteLog(ELogLevel.LOG_INFO, string.Format("{0} in,num = {1}", e.AcceptSocket.RemoteEndPoint, _tokenManager.Count()));
        }

        public void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            try
            {
                if (e.AcceptSocket != null && e.SocketError == SocketError.Success && e.LastOperation == SocketAsyncOperation.Accept)
                {
                    OnAccept(e);
                }
            }
            catch (Exception Ex)
            {
                Log.WriteLog(ELogLevel.LOG_ERROR, string.Format("Accept client {0} error, message: {1}", e.AcceptSocket, Ex.Message));
            }
            finally
            {
                PostAccept(e); //把当前异步事件释放，等待下次连接
            }
        }

        /// <summary>
        /// 接收到消息
        /// </summary>
        /// <param name="msg"></param>
        public void RecvMsg(Msg.MsgPacket msg)
        {
            if (EventRecvMsg != null)
            {
                try
                {
                    EventRecvMsg(msg);
                }
                catch (Exception ex)
                {
                    Log.WriteLog(ELogLevel.LOG_ERROR, ex);
                }
            }
        }

        /// <summary>
        /// 关闭服务器
        /// </summary>
        public void Close()
        {
            if (_sokect != null)
            {
                _sokect.Close();
                _sokect = null;
            }
            if(_heartTimer != null)
            {
                _heartTimer.Stop();
                _heartTimer = null;
            }
        }
    }
}
